#include <avr/io.h>
#include <avr/interrupt.h>

#ifndef F_CPU
#warning "F_CPU was not defined yet, now running with 3686400 Hz"
#define F_CPU 3686400UL     /* 3.6864 Mhz */
//#define F_CPU 8000000UL     /* 8.0000 Mhz */
#endif
#include <util/delay.h>

#include "i2cmaster.h"
#include "uart.h"
#define BAUD 9600UL
#define UBRR_VAL ((F_CPU+BAUD*8)/(BAUD*16)-1)
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD-1000)

#if ( BAUD_ERROR>10)
#error "Baud-Rate causes errors > 1 percent!!!"
#endif

#define LEDSOUT PORTC
#define LEDSCTRL DDRC

#define SHIFT_DDR DDRB
#define SHIFT_PORT PORTB
#define SHIFT_PIN PINB
#define CLK PB5
#define PL PB3
#define DIN PB4

enum STATE_MAIN
{
	eSTATE_MIN,
	eSTATE_INIT,
	eSTATE_CHECK_TWI,
	eSTATE_CONNECT_UART,
	eSTATE_MAINLOOP,
	eSTATE_RESET,
	eSTATE_ERROR
};

enum ERRORS
{
	eERROR_NONE,
	eERROR_BADADR,
	eERROR_BADVALUE,
	eERROR_BADSTATE,
	eERROR_UART_OVERFLOW,
	eERROR_TWI_DISCONNECT,
	eERROR_TWI_OVERFLOW,
	eERROR_TIMEOUT,
	eERROR_MAX
};

enum BITS
{
	eBIT_0 = 0, eBIT_1, eBIT_2, eBIT_3, eBIT_4, eBIT_5, eBIT_6, eBIT_7
};

enum MASK
{
	eMASK_0 = 0x01,
	eMASK_1 = 0x02,
	eMASK_2 = 0x04,
	eMASK_3 = 0x08,
	eMASK_4 = 0x10,
	eMASK_5 = 0x20,
	eMASK_6 = 0x40,
	eMASK_7 = 0x80
};

#define MAX_TWI_DEVICE 63
#define UARTINDICATOR 5
#define TWIINDICATOR 4

enum ERRORS lastError = eERROR_NONE;
enum STATE_MAIN state = eSTATE_MIN;
enum STATE_MAIN laststate = eSTATE_ERROR;
uint8_t g_iLEDState = 0;
uint8_t g_iConnectedSlaves = 0;
uint8_t g_bUARTActive = 0;
uint8_t switches;
uint8_t g_bDeviceAvailable[MAX_TWI_DEVICE + 1];
uint8_t g_iMyID = 0;

/*
 uint8_t segmentcode[10] =
 { 0b11000000, 0b11111001, 0b10100100, 0b10110000, 0b10011001, 0b10010010,
 0b10000010, 0b11111000, 0b10000000, 0b10010000 };
 */
uint8_t segmentcode[10] =
	{ 0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90 };

inline void
setLEDs(uint8_t p_iValue)
{
	LEDSOUT = p_iValue;
	g_iLEDState = p_iValue;
}

inline void
setLEDBit(enum BITS p_iBit, uint8_t p_iValue)
{
	if(p_iValue == 0)
		g_iLEDState &= ~(1 << (uint8_t) p_iBit);
	else
		g_iLEDState |= 1 << (uint8_t) p_iBit;
	;
	LEDSOUT = g_iLEDState;
}

uint8_t
readSerialFromPISO(void)
{
	uint8_t bitcount = 0;
	uint8_t result = 0;
    uint8_t input;

	// prepare correct input/output-settings
	SHIFT_DDR = (1 << CLK) | (1 << PL);

	// set clock to ON (no shift!), ParallelLoad to 0
	SHIFT_PORT = 1<<CLK;
    // now load from input
    _delay_ms(1);
	SHIFT_PORT = (1<<CLK) | (1<<PL); // set PLoad

	while (bitcount < 8)
	{
        // get bit and set result-bit
        result=result<<1;
        input=SHIFT_PIN;
		if( (input & (1<<DIN)) )
          result|=0x01;

		// shift in IC
		SHIFT_PORT = (SHIFT_PIN | (1<<PL)) & ~(1<<CLK); // clear clock
		// shift end
        SHIFT_PORT = SHIFT_PIN | (1<<CLK) | (1<<PL); // set clock
		
        ++bitcount;
	}
	return result;
}

void
init(void)
{
	uint8_t i = 0;

    _delay_ms(100);

	// read TWI-ID from switches
	g_iMyID = readSerialFromPISO();

	// no output
	LEDSCTRL = 0x00; // 0x3f;

	// input only on Switches-Port
	//SWITCHESCTRL = 0x00;
	// get initial switches-state
	//switches = SWITCHESIN;

	// init UART
	uart_init();

	// init TWI
	for (i = 0; i < MAX_TWI_DEVICE; ++i)
		g_bDeviceAvailable[i] = 0;
	i2c_init();
}

void
handleSerialInput(void)
{
	uint8_t b1, b2;

	// get waiting bytes from serial connection
	b1 = uart_getc_wait();
	b2 = uart_getc_wait();
	g_bUARTActive = 1; // temp - should not automatically activate on ANY bytes!
	if(b1 == 0)
	{
		if(state == eSTATE_CONNECT_UART)
		{
			// reset/init in the correct mode
			state = eSTATE_MAINLOOP;
			uart_putc(0);
			uart_putc(0);
		} else
			state = eSTATE_RESET;
	} else if(b1 == 1 || b1 == 3)
	{
		// was a test/ping?
		//uart_putc(1);
		//uart_putc(2);
		g_bUARTActive = 1;
		uart_putc(((g_iMyID & 0x3F) << 2) + 2);
		uart_putc(2);
		//uart_putc(g_iMyID);
	} else
	{
		// TODO 02 parse the incoming bytes
	}
}

int
main(void)
{
	uint8_t i = 0, currentdevice = 0; //oldswitches
	uint8_t iDelayCount = 0;

	// !! WARNING: one must not use I/O until INIT-state passed!!!

	// just prepare UART and TWI
	init();

	// get the TWI-slave-ID


	// interrupts ON
	sei();

	// now loop forever
	while (1)
	{
		// set LEDs according to current state/run-level
		if(state != laststate)
		{
			laststate = state;
			setLEDs(~state);
			//			for (i=0; i<10; ++i)
			//				_delay_ms(50);
		}

		// choose wisely what to do
		switch(state)
		{
			case eSTATE_MIN:
				// this should not happen at all!
				state = eSTATE_INIT;
				break;
			case eSTATE_INIT:
				// reset board and variables
				g_iConnectedSlaves = 0;
				g_bUARTActive = 0;
				state = eSTATE_CHECK_TWI;
				break;
			case eSTATE_CHECK_TWI:
				for (i = 1; i < 64; ++i)
				{
					// signal currently checked slave
					//_delay_ms(50);
					// TWI/I2C-check
					setLEDBit(TWIINDICATOR, i % 2);
					if(i2c_start((i << 1) + I2C_WRITE) == 0) // 0x00 means "okay"?!? FTW!
					{
						// note: empty messages are not allowed, so write at least a byte
						i2c_write(0); // write to 0x00 with reset/invalid value "0x00"
						g_bDeviceAvailable[g_iConnectedSlaves] = i;
						i2c_stop();
						++g_iConnectedSlaves;
					}
				}
				// progress
				state = eSTATE_CONNECT_UART;
				break;

			case eSTATE_CONNECT_UART:
				while (g_bUARTActive == 0)
				{
					if(uart_get_possible() >= 2)
						handleSerialInput();
					else
					{
						_delay_ms(10);
						++iDelayCount;
						if(iDelayCount == 50)
						{
							//uart_putc(0);
							//uart_putc(0);
							setLEDBit(UARTINDICATOR, 1);
						} else if(iDelayCount > 100)
						{
							//uart_putc(0);
							//uart_putc(0);
							setLEDBit(UARTINDICATOR, 0);
							iDelayCount = 0;
						}
					}
				}
				state = eSTATE_MAINLOOP;
				break;
			case eSTATE_MAINLOOP:
				// check for overflow of serial-output (must not happen!)
				if(uart_put_waiting() > 0x3E)
				{
					state = eSTATE_ERROR;
					lastError = eERROR_UART_OVERFLOW;
				}

				if(uart_get_possible() >= 2)
					handleSerialInput();

				// poll currently active slaves
				if(g_iConnectedSlaves > 0)
				{
					currentdevice++;
					if(currentdevice >= g_iConnectedSlaves)
						currentdevice = 0;
					if(i2c_start((i << 1) + I2C_READ) == 0)
					{
						setLEDs(i2c_readNak());
						i2c_stop();
					} else
					{
						// ERROR-Handling - device disconnected?
						state = eSTATE_ERROR;
						lastError = eERROR_TWI_DISCONNECT;
					}
				}

				// TODO 01: push any active commands

				break;
			case eSTATE_RESET:
				//				uart_putc(0);
				//				uart_putc(0);
				state = eSTATE_INIT;
				break;
			case eSTATE_ERROR:
			default:
				// ERROR!
				while (1)
				{
					_delay_ms(10);
					iDelayCount++;
					if(iDelayCount == 50)
					{
						setLEDs(~lastError);
					} else if(iDelayCount > 100)
					{
						setLEDs(0xAA);
						iDelayCount = 0;
					}
				}
				break;
		}
	}

	// pointless, will never be reached
	setLEDs(0xAA);
	return 0;
}
